"""
  Copyright 2008 Texas A&M University Licensed under the
  Educational Community License, Version 2.0 (the "License"); you may
  not use this file except in compliance with the License. You may
  obtain a copy of the License at
  
  http://www.osedu.org/licenses/ECL-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS"
  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  or implied. See the License for the specific language governing
  permissions and limitations under the License.
"""

from datetime import datetime

import zope.interface
import couchdb.schema

from meercat.interfaces import IMutableInstance
from meercat.storage.interfaces import IPersistent
from meercat.storage.couchdb.base import Document
from meercat.storage.couchdb.metadata import Metadata



class Instance(Document):
    zope.interface.implements(IMutableInstance,IPersistent)
    
    active = couchdb.schema.BooleanField()
    source = couchdb.schema.TextField()
    identifier = couchdb.schema.TextField()
    resource_id = couchdb.schema.TextField()
    modified = couchdb.schema.DateTimeField()
    
    def __len__(self):
        """This method should return the number of metadata objects tied to the
        IInstance object
        """
        return len(self.getMetadata())
    
    def __iter__(self):
        """Return an object that implements the iterator interface and iterates
        over the metadata of this resource
        """
        return self.getMetadata()
    
    def __eq__(self, other):
        """Check for equality based on getSource() and getIdentifier() """
        return self.getSource() == other.getSource() and \
               self.getIdentifier() == other.getIdentifier()
    
    def __hash__(self):
        return hash((self.getSource(),self.getIdentifier(),))
    
    def store(self,db=None):
        self.modified = datetime.now()
    
    save = store
    
    def isActive(self):
        """Is this instance active """
        return boolean(self.active)
      
    def getSource(self):
        """Return the source for this instance, where it was harvested from """
        return self.source
    
    def getIdentifier(self):
        """Return the identifier used on the source for this instance """
        return self.identifier
    
    def getMetadata(self):
        """Return an iterable container of objects that implement the IMetadata 
        interface.
        """
        def map_fun(doc):
            if doc.instance_id == self.id:
                return doc
        
        return Metadata.query(self._db,eager=True,
                              map_fun=lambda doc: doc.instance_id == self.id and doc or None)
    
    def getLastModified(self):
        """Return a datetime object for the last time that this instance was 
        modified.
        """
        return self.modified

    def deactivate(self):
        self.active = False
    
    def activate(self):
        self.active = True
    
    def append(self,metadata, autosave=True):
        if not isinstance(Metadata, metadata):
            metadata = Metadata(source=metadata.getSource(),
                                format=metadata.getFormat(),
                                value=metadata.getValue())
        
        metadata.instance_id = self.id
        metadata.store(self._db)
        if autosave:
            self.store()
    
    def remove(self,metadata, autosave=True):
        if not isinstance(Metadata, metadata):
            def map_fun(doc):
               if doc.instance_id == self.id and \
                  doc.source == metadata.getSource() and \
                  doc.format == metadata.getFormat() and \
                  doc.value  == metadata.getValue():
                   return doc
            for m in Metadata.query(self._db,map_fun=map_fun,eager=True):
                self._db.delete(m)
        else:
            self._db.delete(metadata)
        if autosave:
            self.store()
    
    def removeFromSource(self,source,autosave=True):
        for m in Metadata.query(self._db,
                                map_fun=lambda doc:(doc.instance_id == self.id and 
                                                    doc.source == source) and \
                                                   doc or None):
            self._db.delete(m)
        self.store()
            